
/***************************************************************************************************
**
** Real-Time Hierarchical Profiling for Game Programming Gems 3
**
** by Greg Hjelstrom & Byon Garrabrant
**
***************************************************************************************************/

// Credits: The Clock class was inspired by the Timer classes in
// Ogre (www.ogre3d.org).



#ifndef QUICK_PROF_H
#define QUICK_PROF_H

//To disable built-in profiling, please comment out next line
//#define BT_NO_PROFILE 1
#ifndef BT_NO_PROFILE

#include "btScalar.h"
#include "LinearMath/btAlignedAllocator.h"
#include <new>




//if you don't need btClock, you can comment next line
#define USE_BT_CLOCK 1

#ifdef USE_BT_CLOCK
#ifdef __CELLOS_LV2__
#include <sys/sys_time.h>
#include <sys/time_util.h>
#include <stdio.h>
#endif

#if defined (SUNOS) || defined (__SUNOS__)
#include <stdio.h>
#endif

#if defined(WIN32) || defined(_WIN32)

#define USE_WINDOWS_TIMERS
#define WIN32_LEAN_AND_MEAN
#define NOWINRES
#define NOMCX
#define NOIME
#ifdef _XBOX
#include <Xtl.h>
#else
#include <windows.h>
#endif
#include <time.h>

#else
#include <sys/time.h>
#endif

#define mymin(a,b) (a > b ? a : b)

///The btClock is a portable basic clock that measures accurate time in seconds, use for profiling.
class btClock
{
public:
    btClock()
    {
#ifdef USE_WINDOWS_TIMERS
        QueryPerformanceFrequency(&mClockFrequency);
#endif
        reset();
    }

    ~btClock()
    {
    }

    /// Resets the initial reference time.
    void reset()
    {
#ifdef USE_WINDOWS_TIMERS
        QueryPerformanceCounter(&mStartTime);
        mStartTick = GetTickCount();
        mPrevElapsedTime = 0;
#else
#ifdef __CELLOS_LV2__

        typedef uint64_t  ClockSize;
        ClockSize newTime;
        //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
        SYS_TIMEBASE_GET(newTime);
        mStartTime = newTime;
#else
        gettimeofday(&mStartTime, 0);
#endif

#endif
    }

    /// Returns the time in ms since the last call to reset or since
    /// the btClock was created.
    unsigned long int getTimeMilliseconds()
    {
#ifdef USE_WINDOWS_TIMERS
        LARGE_INTEGER currentTime;
        QueryPerformanceCounter(&currentTime);
        LONGLONG elapsedTime = currentTime.QuadPart -
                               mStartTime.QuadPart;

        // Compute the number of millisecond ticks elapsed.
        unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
                                  mClockFrequency.QuadPart);

        // Check for unexpected leaps in the Win32 performance counter.
        // (This is caused by unexpected data across the PCI to ISA
        // bridge, aka south bridge.  See Microsoft KB274323.)
        unsigned long elapsedTicks = GetTickCount() - mStartTick;
        signed long msecOff = (signed long)(msecTicks - elapsedTicks);
        if (msecOff < -100 || msecOff > 100)
        {
            // Adjust the starting time forwards.
            LONGLONG msecAdjustment = mymin(msecOff *
                                            mClockFrequency.QuadPart / 1000, elapsedTime -
                                            mPrevElapsedTime);
            mStartTime.QuadPart += msecAdjustment;
            elapsedTime -= msecAdjustment;

            // Recompute the number of millisecond ticks elapsed.
            msecTicks = (unsigned long)(1000 * elapsedTime /
                                        mClockFrequency.QuadPart);
        }

        // Store the current elapsed time for adjustments next time.
        mPrevElapsedTime = elapsedTime;

        return msecTicks;
#else

#ifdef __CELLOS_LV2__
        uint64_t freq = sys_time_get_timebase_frequency();
        double dFreq = ((double) freq) / 1000.0;
        typedef uint64_t  ClockSize;
        ClockSize newTime;
        SYS_TIMEBASE_GET(newTime);
        //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");

        return (unsigned long int)((double(newTime - mStartTime)) / dFreq);
#else

        struct timeval currentTime;
        gettimeofday(&currentTime, 0);
        return (currentTime.tv_sec - mStartTime.tv_sec) * 1000 +
               (currentTime.tv_usec - mStartTime.tv_usec) / 1000;
#endif //__CELLOS_LV2__
#endif
    }

    /// Returns the time in us since the last call to reset or since
    /// the Clock was created.
    unsigned long int getTimeMicroseconds()
    {
#ifdef USE_WINDOWS_TIMERS
        LARGE_INTEGER currentTime;
        QueryPerformanceCounter(&currentTime);
        LONGLONG elapsedTime = currentTime.QuadPart -
                               mStartTime.QuadPart;

        // Compute the number of millisecond ticks elapsed.
        unsigned long msecTicks = (unsigned long)(1000 * elapsedTime /
                                  mClockFrequency.QuadPart);

        // Check for unexpected leaps in the Win32 performance counter.
        // (This is caused by unexpected data across the PCI to ISA
        // bridge, aka south bridge.  See Microsoft KB274323.)
        unsigned long elapsedTicks = GetTickCount() - mStartTick;
        signed long msecOff = (signed long)(msecTicks - elapsedTicks);
        if (msecOff < -100 || msecOff > 100)
        {
            // Adjust the starting time forwards.
            LONGLONG msecAdjustment = mymin(msecOff *
                                            mClockFrequency.QuadPart / 1000, elapsedTime -
                                            mPrevElapsedTime);
            mStartTime.QuadPart += msecAdjustment;
            elapsedTime -= msecAdjustment;
        }

        // Store the current elapsed time for adjustments next time.
        mPrevElapsedTime = elapsedTime;

        // Convert to microseconds.
        unsigned long usecTicks = (unsigned long)(1000000 * elapsedTime /
                                  mClockFrequency.QuadPart);

        return usecTicks;
#else

#ifdef __CELLOS_LV2__
        uint64_t freq = sys_time_get_timebase_frequency();
        double dFreq = ((double) freq) / 1000000.0;
        typedef uint64_t  ClockSize;
        ClockSize newTime;
        //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory");
        SYS_TIMEBASE_GET(newTime);

        return (unsigned long int)((double(newTime - mStartTime)) / dFreq);
#else

        struct timeval currentTime;
        gettimeofday(&currentTime, 0);
        return (currentTime.tv_sec - mStartTime.tv_sec) * 1000000 +
               (currentTime.tv_usec - mStartTime.tv_usec);
#endif//__CELLOS_LV2__
#endif
    }

private:
#ifdef USE_WINDOWS_TIMERS
    LARGE_INTEGER mClockFrequency;
    DWORD mStartTick;
    LONGLONG mPrevElapsedTime;
    LARGE_INTEGER mStartTime;
#else
#ifdef __CELLOS_LV2__
    uint64_t	mStartTime;
#else
    struct timeval mStartTime;
#endif
#endif //__CELLOS_LV2__

};

#endif //USE_BT_CLOCK




///A node in the Profile Hierarchy Tree
class	CProfileNode
{

public:
    CProfileNode(const char * name, CProfileNode * parent);
    ~CProfileNode(void);

    CProfileNode * Get_Sub_Node(const char * name);

    CProfileNode * Get_Parent(void)
    {
        return Parent;
    }
    CProfileNode * Get_Sibling(void)
    {
        return Sibling;
    }
    CProfileNode * Get_Child(void)
    {
        return Child;
    }

    void				CleanupMemory();
    void				Reset(void);
    void				Call(void);
    bool				Return(void);

    const char *	Get_Name(void)
    {
        return Name;
    }
    int				Get_Total_Calls(void)
    {
        return TotalCalls;
    }
    float				Get_Total_Time(void)
    {
        return TotalTime;
    }

protected:

    const char *	Name;
    int				TotalCalls;
    float				TotalTime;
    unsigned long int			StartTime;
    int				RecursionCounter;

    CProfileNode *	Parent;
    CProfileNode *	Child;
    CProfileNode *	Sibling;
};

///An iterator to navigate through the tree
class CProfileIterator
{
public:
    // Access all the children of the current parent
    void				First(void);
    void				Next(void);
    bool				Is_Done(void);
    bool                Is_Root(void)
    {
        return (CurrentParent->Get_Parent() == 0);
    }

    void				Enter_Child(int index);		// Make the given child the new parent
    void				Enter_Largest_Child(void);	// Make the largest child the new parent
    void				Enter_Parent(void);			// Make the current parent's parent the new parent

    // Access the current child
    const char *	Get_Current_Name(void)
    {
        return CurrentChild->Get_Name();
    }
    int				Get_Current_Total_Calls(void)
    {
        return CurrentChild->Get_Total_Calls();
    }
    float				Get_Current_Total_Time(void)
    {
        return CurrentChild->Get_Total_Time();
    }

    // Access the current parent
    const char *	Get_Current_Parent_Name(void)
    {
        return CurrentParent->Get_Name();
    }
    int				Get_Current_Parent_Total_Calls(void)
    {
        return CurrentParent->Get_Total_Calls();
    }
    float				Get_Current_Parent_Total_Time(void)
    {
        return CurrentParent->Get_Total_Time();
    }

protected:

    CProfileNode *	CurrentParent;
    CProfileNode *	CurrentChild;

    CProfileIterator(CProfileNode * start);
    friend	class		CProfileManager;
};


///The Manager for the Profile system
class	CProfileManager
{
public:
    static	void						Start_Profile(const char * name);
    static	void						Stop_Profile(void);

    static	void						CleanupMemory(void)
    {
        Root.CleanupMemory();
    }

    static	void						Reset(void);
    static	void						Increment_Frame_Counter(void);
    static	int						Get_Frame_Count_Since_Reset(void)
    {
        return FrameCounter;
    }
    static	float						Get_Time_Since_Reset(void);

    static	CProfileIterator *	Get_Iterator(void)
    {

        return new CProfileIterator(&Root);
    }
    static	void						Release_Iterator(CProfileIterator * iterator)
    {
        delete(iterator);
    }

    static void	dumpRecursive(CProfileIterator* profileIterator, int spacing);

    static void	dumpAll();

private:
    static	CProfileNode			Root;
    static	CProfileNode *			CurrentNode;
    static	int						FrameCounter;
    static	unsigned long int					ResetTime;
};


///ProfileSampleClass is a simple way to profile a function's scope
///Use the BT_PROFILE macro at the start of scope to time
class	CProfileSample
{
public:
    CProfileSample(const char * name)
    {
        CProfileManager::Start_Profile(name);
    }

    ~CProfileSample(void)
    {
        CProfileManager::Stop_Profile();
    }
};


#define	BT_PROFILE( name )			CProfileSample __profile( name )

#else

#define	BT_PROFILE( name )

#endif //#ifndef BT_NO_PROFILE



#endif //QUICK_PROF_H


